package com.csnt.dblocation.httpclient;


import io.micrometer.core.instrument.util.StringUtils;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.NameValuePair;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.ResponseHandler;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.client.utils.HttpClientUtils;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.ssl.SSLContextBuilder;
import org.apache.http.ssl.TrustStrategy;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import java.io.IOException;
import java.net.URI;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * http or https 请求工具类
 */
@Component
public class HttpClientUtil{

    private static Logger log= LoggerFactory.getLogger(HttpClientUtil.class);

    /**
     * POST JSON 请求
     * @param url
     * @param jsonData 参数为JSON字符串
     * @return
     */
    public  String doJsonPost(String url,String jsonData){
        if(StringUtils.isBlank(url)){
            throw new NullPointerException("doJsonPost request url not allow null or ''! ");
        }
        long beginTime=System.currentTimeMillis();
        HttpPost httpPost = new HttpPost(url);
        try {
            httpPost.addHeader("Content-type","application/json; charset=utf-8");
            httpPost.setEntity(new StringEntity(jsonData, StandardCharsets.UTF_8));
            String response=InnerHttpClient.sendRequest(httpPost);
            long endTime=System.currentTimeMillis();
            log.info("doJsonPost request url:{} successed!reqParams is :{},response content is :{},time-consuming is:{}ms!",url,jsonData,response,endTime-beginTime);
            return response;
        } catch (Exception e) {
            log.error("doJsonPost request url:{} failed!reqParams is :{},exception content is :",url,jsonData,e);
            return null;
        }
    }

    /**
     * POST 请求,可附带参数
     * @param url
     * @param reqParams
     * @return
     */
    public  String doPost(String url, Map<String, String> reqParams){
        if(StringUtils.isBlank(url)){
            throw new NullPointerException("doPost request url not allow null or ''! ");
        }
        long beginTime=System.currentTimeMillis();
        List<NameValuePair> nvps = new ArrayList<NameValuePair>();
        if(reqParams!=null&&!reqParams.isEmpty()){
            for(String k:reqParams.keySet()){
                nvps.add(new BasicNameValuePair(k,reqParams.get(k)));
            }
        }
        HttpPost httpPost = new HttpPost(url);
        try {
            httpPost.setEntity(new UrlEncodedFormEntity(nvps,StandardCharsets.UTF_8));
            String response=InnerHttpClient.sendRequest(httpPost);
            long endTime=System.currentTimeMillis();
            log.info("doPost request url:{} successed!reqParams is :{},response content is :{},time-consuming is:{}ms!",url,reqParams,response,endTime-beginTime);
            return response;
        } catch (Exception e) {
            log.error("doPost request url:{} failed!reqParams is :{},exception content is :",url,reqParams,e);
            return null;
        }
    }

    /**
     * GET 请求
     * @param url
     * @return
     */
    public  String doGet(String url){
        return doGet(url, null);
    }

    /**
     * GET 请求，附带有提供的参数
     * @param url
     * @param reqParams 请求参数
     * @return
     */
    public  String doGet(String url, Map<String, String> reqParams){
        if(StringUtils.isBlank(url)){
            throw new NullPointerException("doGet request url not allow null or ''! ");
        }
        long beginTime=System.currentTimeMillis();
        StringBuilder builder=new StringBuilder();
        if(reqParams!=null&&!reqParams.isEmpty()){
            for(String k:reqParams.keySet()){
                if(builder.length()>0){
                    builder.append("&");
                }
                builder.append(k).append("=");
                if(StringUtils.isNotBlank(reqParams.get(k))){
                    builder.append(reqParams.get(k));
                }
            }
        }
        String params=builder.toString();
        if(params.length()>0){
            url+=url.indexOf("?")!=-1?"&"+params:"?"+params;
        }
        try {
            URL u=new URL(url);
            HttpGet httpGet=new HttpGet(new URI(u.getProtocol(),u.getUserInfo(),u.getHost(),u.getPort(),u.getPath(), u.getQuery(),null));
            String response=InnerHttpClient.sendRequest(httpGet);
            long endTime=System.currentTimeMillis();
            log.info("doGet request url:{} successed!response content is :{},time-consuming is:{}ms!",url,response,endTime-beginTime);
            return response;
        } catch (Exception e) {
            log.error("doGet request url:{} failed!exception content is :",url,e);
            return null;
        }
    }

    /**
     * 请求客户端实现类
     */
    static class InnerHttpClient{
        //http clilent中从connetcion pool中获得一个connection的超时时间
        final static int REQUEST_TIME_OUT=5000;
        //链接建立的超时时间
        final static int CONNECT_TIME_OUT=10000;
        //响应超时时间，超过此时间不再读取响应
        final static int SOCKET_TIME_OUT=60000;
        /**
         * 请求配置
         * @return
         */
        static RequestConfig config(){
            return RequestConfig.custom()
                    .setConnectionRequestTimeout(0)
                    .setConnectTimeout(0)
                    .setSocketTimeout(0)
                    .build();
        }
        /**
         * 设置请求协议，针对HTTPS协议信任所有证书，同时忽略对HOSTNAME的校验
         * @return
         * @throws KeyManagementException
         * @throws NoSuchAlgorithmException
         * @throws KeyStoreException
         */
        static Registry<ConnectionSocketFactory> registry() throws Exception{
            SSLContext sslcontext = new SSLContextBuilder().loadTrustMaterial(new TrustStrategy() {
                public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException {
                    return true;
                }
            }).build();
            // Create a registry of custom connection socket factories for supported
            // protocol schemes.
            return RegistryBuilder.<ConnectionSocketFactory>create()
                    .register("http", PlainConnectionSocketFactory.INSTANCE)
                    .register("https", new SSLConnectionSocketFactory(sslcontext,new HostnameVerifier() {

                        @Override
                        public boolean verify(String hostname, SSLSession session) {
                            return true;
                        }

                    })).build();
        }
        /**
         * connect链接池管理
         * @return
         * @throws KeyManagementException
         * @throws NoSuchAlgorithmException
         * @throws KeyStoreException
         */
        static PoolingHttpClientConnectionManager connectManager() throws Exception{
            return new PoolingHttpClientConnectionManager(registry());
        }
        /**
         * 创建请求客户端
         * @return
         * @throws Exception
         */
        static HttpClient create() throws Exception{
            return HttpClients.custom()
                    .setDefaultRequestConfig(config())
                    .setConnectionManager(connectManager()).build();
        }

        /**
         * 发送请求模板
         * @param requestDeal
         * @return
         * @throws Exception
         */
        static String sendRequest(HttpUriRequest uriRequest) throws Exception{
            HttpClient httpClient=create();
            try {
                // Create a custom response handler
                ResponseHandler<String> responseHandler = new ResponseHandler<String>() {

                    @Override
                    public String handleResponse(final HttpResponse response) throws ClientProtocolException, IOException {
                        int status = response.getStatusLine().getStatusCode();
                        if (status >= HttpStatus.SC_OK && status < HttpStatus.SC_MULTIPLE_CHOICES) {
                            HttpEntity entity = response.getEntity();
                            return entity != null ? EntityUtils.toString(entity,StandardCharsets.UTF_8) : null;
                        } else {
                            throw new ClientProtocolException("Unexpected response status: " + status);
                        }
                    }

                };
                return httpClient.execute(uriRequest, responseHandler);
            } catch (Exception e) {
                throw e;
            }finally{
                HttpClientUtils.closeQuietly(httpClient);
            }
        }
    }
}
